﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MergulhoGrafo.QuadriParticao
{
    public class ParticaoMl
    {
        #region Atributos
        public List<string> Estrela;
        public List<int> Poligono;
        public List<Tuple<int, int>> SetorAngulo;
        private List<Nodo> _ListaNodoEstrela;
        private List<Tuple<Nodo, int>> _listaNodoSetor;
        private List<Nodo> _ListaNodoPoligono;
        public List<Tuple<int, bool>> ListaAnguloOrientavel;
        #endregion

        #region Construtor

        public ParticaoMl(GrafoRetangulo grafoRetangulo, QuadriParticao quadriParticao)
        {
            this.Estrela = new List<string>();
            this.Poligono = new List<int>();
            grafoRetangulo.ContrairGrafo(quadriParticao.particaoA.Retangulos);
            this.ConstriuirEstrelaNodos(grafoRetangulo.Retangulos, quadriParticao);
            this.construirPoligono(quadriParticao);
            this.ConstruirSetorAngulo(grafoRetangulo.Retangulos, quadriParticao);
        }

        #endregion

        #region Métodos 

        private void ConstriuirEstrelaNodos(List<Retangulo> newRetangles, QuadriParticao quadriParticao)
        {
            List<Nodo> listaNodos = new List<Nodo>();

            Nodo nodoAtual = GetNodo(newRetangles, quadriParticao);
            Nodo nodoInicial = nodoAtual;
            
            if (nodoAtual != null)
            {
                do
                {
                    //Adicionar o nodo na lista
                    listaNodos.Add(nodoAtual);
                    listaNodos.Add(nodoAtual.Curto);

                    nodoAtual = nodoAtual.Curto.Link;
                }
                while (nodoAtual.GetNumeroRef() != nodoInicial.GetNumeroRef());                
            }
            else
            {
                throw new Exception("Erro na Construção de Lista Estrela de Nodos");
            }

            // Variáveis para construir setor - angulo
            this._ListaNodoEstrela = listaNodos;

            for (int i = 0; i < listaNodos.Count; i=i+2)
            {                
                if (listaNodos[i].Pai.numeroRef * 4 - 2 < listaNodos[i].GetNumeroRef())
                {
                    this.Estrela.Add(listaNodos[i].Pai.numeroRef+" c");
                }
                else
                {
                    this.Estrela.Add(listaNodos[i].Pai.numeroRef + " a");
                }
            }

        }

        private void construirPoligono(QuadriParticao quadriParticao)
        {
            for (int i = 0; i < this.Estrela.Count; i++)
            {
                string[] estrela = this.Estrela[i].Split(' ');
                int index = Convert.ToInt32(estrela[0]);

                if (!quadriParticao.particaoC.Retangulos.Contains(index))
                {
                    this.Poligono.Add(index);
                }
            }
        }

        public List<Tuple<string, double>> PosicaoPontosBorda()
        {
            List<Tuple<string, double>> retorno = new List<Tuple<string, double>>();
            double posicaoAtual = 0;
            double incremento = Math.PI * 2 / this.Poligono.Count;
            List<string> temp = new List<string>();

            foreach (string i in this.Estrela)
            {
                string[] estrela = i.Split(' ');
                int index = Convert.ToInt32(estrela[0]);

                if (this.Poligono.Contains(index))
                {
                    if (temp.Count > 0)
                    {
                        double incrementoTemp = incremento / (temp.Count+1);
                        double posicaoTemp = (posicaoAtual-incremento) + incrementoTemp;
                        foreach (string j in temp)
                        {
                            retorno.Add(new Tuple<string, double>(j, posicaoTemp));
                            posicaoTemp += incrementoTemp;
                        }

                        temp.Clear();
                    }

                    retorno.Add(new Tuple<string, double>(i, posicaoAtual));
                    posicaoAtual += incremento;
                }
                else
                {
                    temp.Add(i);
                }
            }

            if (temp.Count > 0)
            {
                double incrementoTemp = incremento / (temp.Count + 1);
                double posicaoTemp = (posicaoAtual - incremento) + incrementoTemp;
                
                foreach (string j in temp)
                {
                    retorno.Add(new Tuple<string, double>(j, posicaoTemp));
                    posicaoTemp += incrementoTemp;
                }

                temp.Clear();
            }

            return retorno;
        }

        private Nodo GetNodo(List<Retangulo> newRetangles, QuadriParticao quadriParticao)
        {
            foreach (Retangulo ret in newRetangles)
            {
                if (!quadriParticao.particaoA.Retangulos.Contains(ret.numeroRef))
                {
                    return ret.NodoDirIni();
                }
            }

            return null;
        }

        public Tuple<double[,],List<List<string>>> MatrizPosicoesCentroDisco(GrafoRetangulo grafoRetangulo, QuadriParticao quadriParticao)
        {
            double[,] retorno = new double[grafoRetangulo.numeroVertices, grafoRetangulo.numeroVertices];

            List < List<string>> matrizB = new List<List<string>>();

            for (int i = 1; i <= grafoRetangulo.numeroVertices; i++)
            {
                List<Retangulo> resultado =
                    (from gr in grafoRetangulo.Retangulos where
                         (gr.Vert1 == i || gr.Vert2 == i)
                     select gr).ToList();

                retorno[i-1, i-1] = resultado.Count;
                List<string> bs_temp = new List<string>();
                matrizB.Add(bs_temp);
                foreach (Retangulo ret in resultado)
                {
                    
                    if (quadriParticao.particaoC.Retangulos.Contains(ret.numeroRef)
                        || quadriParticao.particaoD.Retangulos.Contains(ret.numeroRef))
                    {
                        if (ret.Vert1 == i)
                        {
                            matrizB[i - 1].Add(ret.numeroRef + " a");
                        }
                        else
                        {
                            matrizB[i - 1].Add(ret.numeroRef + " c");
                        }                        
                    }
                    else
                    {
                        if (ret.Vert1 == i)
                        {
                            retorno[i-1, ret.Vert2-1] = -1;
                        }
                        else
                        {
                            retorno[i-1, ret.Vert1-1] = -1;
                        }
                    }
                }
            }

            return new Tuple<double[,],List<List<string>>>(retorno,matrizB);
        }

        public void ConstruirSetorAngulo(List<Retangulo> newRetangles, QuadriParticao quadriParticao)
        {
            #region ListaNodoPoligono

            List<Nodo> listaNodoPoligono = new List<Nodo>();

            foreach (Nodo nodo in this._ListaNodoEstrela)
            {
                if (this.Poligono.Contains(nodo.Pai.numeroRef))
                {
                    listaNodoPoligono.Add(nodo);
                }
            }

            this._ListaNodoPoligono = listaNodoPoligono;
            #endregion

            #region ListaNodoSetor
            List<Tuple<Nodo, int>> listaNodoSetor = new List<Tuple<Nodo, int>>();

            int setor = 0;

            // Primeiro e Último pertence ao setor 0
            // verificar se ehTorcida

            listaNodoSetor.Add(new Tuple<Nodo, int>(listaNodoPoligono[0], setor));
            setor++;

            bool Proximo = false;

            for (int i = 1; i < listaNodoPoligono.Count; i = i + 2)
            {
                Proximo = false;
                int j = i;
                int count = 0;

                while (!Proximo)
                {
                    listaNodoSetor.Add(new Tuple<Nodo, int>(listaNodoPoligono[j], setor));
                    count++;
                    j++;

                    if (count == 2 || j >= listaNodoPoligono.Count)
                    {
                        Proximo = true;
                    }

                    /*
                    if (this.Poligono.Contains(this._ListaNodoEstrela[j].Pai.numeroRef))
                    {
                        listaNodoSetor.Add(new Tuple<Nodo, int>(this._ListaNodoEstrela[j], setor));
                        count++;
                    }
                    else
                    {
                        i = i + 1;
                    }
                    j++;
                    if (count == 2 || j >= this._ListaNodoEstrela.Count)
                    {
                        Proximo = true;
                    }
                    */
                }

                setor++;
            }

            //Último setor zero
            Tuple<Nodo, int> NodoUltimo = listaNodoSetor.Last();
            listaNodoSetor.RemoveAt(listaNodoSetor.Count - 1);
            Tuple<Nodo, int> newNodoUltimo = new Tuple<Nodo, int>(NodoUltimo.Item1, 0);
            listaNodoSetor.Add(newNodoUltimo);
            this._listaNodoSetor = listaNodoSetor;

            #endregion

            #region ListaSetorAngulo

            this.SetorAngulo = new List<Tuple<int, int>>();
            List<Tuple<int, bool>> listaAnguloOrientavel = new List<Tuple<int, bool>>();

            Nodo nodoAtual = this._listaNodoSetor[0].Item1;
            Nodo nodoInicial = nodoAtual;
            bool EhOrientavel = true;
            int angulo = 0;

            do
            {
                //Linkar setor com angulo atual
                Tuple<Nodo, int> temp = this._listaNodoSetor.Find(p => p.Item1.GetNumeroRef() == nodoAtual.GetNumeroRef());

                if (temp == null)
                {
                    Nodo nodoTemp = nodoAtual;
                    nodoAtual = nodoAtual.Link;

                    // tem que verificar se link do nodo atual é do poligono
                    if (!this.Poligono.Contains(nodoAtual.Pai.numeroRef))
                    {
                        nodoAtual = nodoAtual.Curto.Link;
                    }
                    
                    temp = this._listaNodoSetor.Find(p => p.Item1.GetNumeroRef() == nodoAtual.GetNumeroRef());
                    nodoAtual = nodoTemp;
                }
                
                // Tupla Setor - Angulo
                SetorAngulo.Add(new Tuple<int,int>(temp.Item2, angulo));

                listaAnguloOrientavel.Add(new Tuple<int, bool>(angulo, EhOrientavel));

                if (temp.Item1.Pai.EhTorcida)
                {
                    EhOrientavel = !EhOrientavel;
                }

                nodoAtual = nodoAtual.Longo.Link;
                
                //Pode ser que o link não seja para um nodo do poligono
                while (!this.Poligono.Contains(nodoAtual.Pai.numeroRef))
                {
                    nodoAtual = nodoAtual.Curto.Link;
                }

                //Incrementar angulo
                angulo++;
            }
            while (nodoAtual.GetNumeroRef() != nodoInicial.GetNumeroRef());

            ListaAnguloOrientavel = listaAnguloOrientavel;
            #endregion

        }

        #endregion
    }
}
